File: /var/www/html/wp-content/plugins/wp-recipe-maker/includes/public/class-wprm-print.php
<?php
/**
* Handle the recipe printing.
*
* @link https://bootstrapped.ventures
* @since 1.0.0
*
* @package WP_Recipe_Maker
* @subpackage WP_Recipe_Maker/includes/public
*/
/**
* Handle the recipe printing.
*
* @since 1.0.0
* @package WP_Recipe_Maker
* @subpackage WP_Recipe_Maker/includes/public
* @author Brecht Vandersmissen <brecht@bootstrapped.ventures>
*/
class WPRM_Print {
/**
* Register actions and filters.
*
* @since 1.0.0
*/
public static function init() {
add_action( 'init', array( __CLASS__, 'print_page' ) );
add_filter( 'wprm_print_output', array( __CLASS__, 'output_first' ), 1, 2 );
add_filter( 'wprm_print_output', array( __CLASS__, 'output_last' ), 999, 2 );
}
/**
* Get the print slug.
*
* @since 6.1.0
*/
public static function slug() {
$slug = WPRM_Settings::get( 'print_slug' );
if ( ! $slug ) {
$slug = WPRM_Settings::get_default( 'print_slug' );
}
return $slug;
}
/**
* Check if someone is trying to reach the print page.
*
* @since 1.3.0
*/
public static function print_page() {
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
preg_match( '/[\/\?]' . self::slug() . '[\/=](.+?)(\/)?$/', $request_uri, $print_url ); // Input var okay.
$print_query = isset( $print_url[1] ) ? $print_url[1] : '';
// Get individual print args.
$print_query = str_ireplace( '?', '/', $print_query );
$print_query = str_ireplace( '&', '/', $print_query );
$print_args = $print_query ? explode( '/', $print_query ) : array();
// Backwards compatibility with old /wprm_print/123 URL
if ( $print_args && '' . intval( $print_args[0] ) === $print_args[0] ) {
$print_args = array_merge(
array( 'recipe' ),
$print_args
);
}
// Assume we're printing a single recipe if only 1 argument is set.
$print_arg_options = apply_filters( 'wprm_print_arg_options', array( 'recipe', 'recipes', 'collection', 'shopping-list' ) );
if ( $print_args && ! in_array( $print_args[0], $print_arg_options ) ) {
$print_args = array_merge(
array( 'recipe' ),
$print_args
);
}
if ( $print_args && 1 <= count( $print_args ) && $print_args[0] ) {
WPRM_Context::set( 'print' );
// Convert slug to ID if we're printing a single recipe.
if ( 'recipe' === $print_args[0] && 'slug' === WPRM_Settings::get( 'print_recipe_identifier' ) ) {
// Don't do anything if it's actually an ID. Unless that ID is not actually a recipe.
if ( '' . intval( $print_args[1] ) !== $print_args[1] || WPRM_POST_TYPE !== get_post_type( intval( $print_args[1] ) ) ) {
$slug = $print_args[1];
// Test with wprm- prefix first, as that could have been removed.
$recipe = get_page_by_path( 'wprm-' . $slug, OBJECT, WPRM_POST_TYPE );
if ( ! $recipe ) {
$recipe = get_page_by_path( $slug, OBJECT, WPRM_POST_TYPE );
}
if ( $recipe ) {
$print_args[1] = $recipe->ID;
}
}
}
// Get assets to output while making sure nothing gets output yet.
ob_start();
$output = apply_filters( 'wprm_print_output', array(
'type' => false,
'assets' => array(),
'recipe_ids' => array(),
''
), $print_args );
$prevent_from_getting_output = ob_get_contents();
ob_end_clean();
if ( $output && $output['type'] ) {
// Prevent WP Rocket lazy image loading on print page.
add_filter( 'do_rocket_lazyload', '__return_false' );
// Prevent Avada lazy image loading on print page.
if ( class_exists( 'Fusion_Images' ) && property_exists( 'Fusion_Images', 'lazy_load' ) ) {
Fusion_Images::$lazy_load = false;
}
// Allow overriding of template file.
$print_file = apply_filters( 'wprm_print_output_template_file', apply_filters( 'wprm_print_file', WPRM_DIR . 'templates/public/print.php' ), $output, $print_args );
// Load print template file.
header( 'HTTP/1.1 200 OK' );
require( $print_file );
flush();
exit;
} else {
// Redirect to homepage.
wp_redirect( home_url() );
exit();
}
}
}
/**
* Get output for the print page with high priority.
*
* @since 6.1.0
* @param array $output Current output for the print page.
* @param array $args Arguments for the print page.
*/
public static function output_first( $output, $args ) {
$is_pdf_download_page = apply_filters( 'wprm_print_is_pdf_download_page', false, $args );
// Default assets to load.
$output['assets'][] = array(
'type' => 'css',
'url' => WPRM_URL . 'dist/public-' . WPRM_Settings::get( 'recipe_template_mode' ) . '.css',
'version' => WPRM_VERSION,
);
$output['assets'][] = array(
'type' => 'css',
'url' => WPRM_URL . 'dist/print.css',
'version' => WPRM_VERSION,
);
$output['assets'][] = array(
'type' => 'js',
'url' => WPRM_URL . 'dist/print.js',
'version' => WPRM_VERSION,
);
$output['assets'][] = array(
'type' => 'custom',
'html' => '<style>' . WPRM_Assets::get_custom_css( 'print' ) . '</style>',
);
$output['assets'][] = array(
'type' => 'custom',
'html' => self::print_accent_color_styling(),
);
$output['assets'][] = array(
'type' => 'custom',
'html' => '<script>var wprm_print_settings = ' . wp_json_encode( array(
'print_remove_links' => WPRM_Settings::get( 'print_remove_links' ),
'print_new_tab' => WPRM_Settings::get( 'print_new_tab' ),
'pdf_download_enabled' => WPRM_Settings::get( 'pdf_download_enabled' ),
'utilities_endpoint' => rtrim( get_rest_url( null, 'wp-recipe-maker/v1/utilities' ), '/' ),
'nonce' => wp_create_nonce( 'wprm' ),
'auto_pdf_download' => $is_pdf_download_page,
) ) . ';</script>',
);
if ( WPRM_Settings::get( 'print_recipe_page_break' ) ) {
$output['assets'][] = array(
'type' => 'custom',
'html' => '<style>@media print { .wprm-print-recipe + .wprm-print-recipe { page-break-before: always; } }</style>',
);
}
// Printing a single recipe.
if ( 'recipe' === $args[0] ) {
$recipe_id = intval( $args[1] );
if ( WPRM_POST_TYPE === get_post_type( $recipe_id ) ) {
$recipe = WPRM_Recipe_Manager::get_recipe( $recipe_id );
if ( ! self::has_permission( $recipe ) ) {
return $output;
}
// Get template to output.
$template = false;
if ( isset( $args[2] ) ) {
$template_slug = sanitize_key( $args[2] );
$template = WPRM_Template_Manager::get_template_by_slug( $template_slug );
}
// Use default print template if no specific template set.
if ( ! $template ) {
$template = WPRM_Template_Manager::get_template_by_type( 'print', $recipe->type() );
}
// Add styling for this recipe's print template.
$output['assets'][] = array(
'type' => 'custom',
'html' => '<style type="text/css">' . WPRM_Template_Manager::get_template_css( $template ) . '</style>',
);
// Add options to header.
$output['header'] = self::print_header_images( $recipe );
$output['type'] = 'recipe';
$output['recipe'] = $recipe;
$output['recipe_ids'][] = $recipe_id;
$output['title'] = $recipe->name() . ' - ' . get_bloginfo( 'name' );
$output['url'] = $recipe->permalink();
$output['html'] = '<div id="wprm-print-recipe-0" data-recipe-id="' . $recipe_id . '" class="wprm-print-recipe wprm-print-recipe-' . $recipe_id . '" data-servings="' . esc_attr( $recipe->servings() ) . '">' . WPRM_Template_Manager::get_template( $recipe, 'print', $template['slug'] ) . '</div>';
}
}
// Printing a list of recipes.
if ( 'recipes' === $args[0] ) {
$recipes = array();
$recipe_ids = self::decode_ids( $args[1] );
if ( $recipe_ids ) {
$all_recipes = array();
for ( $i = 0; $i < count( $recipe_ids ); $i += 2 ) {
// Servings passed along as large number to support decimals.
$servings_passed = floatval( $recipe_ids[ $i + 1 ] );
$servings = $servings_passed / pow( 10, 6 );
// Prevent most backwards compatibility issues.
if ( $servings_passed < 100 ) {
$servings = $servings_passed;
}
$all_recipes[] = array(
'id' => intval( $recipe_ids[ $i ] ),
'servings' => $servings,
);
}
// Maybe remove duplicates.
if ( WPRM_Settings::get( 'recipe_collections_print_recipes_multiple_times' ) ) {
$unique_recipes = $all_recipes;
} else {
$serialized = array_map( 'serialize', $all_recipes );
$unique = array_unique( $serialized );
$unique_recipes = array_intersect_key( $all_recipes, $unique );
}
// Get template to output.
$custom_template = false;
if ( isset( $args[2] ) ) {
$template_slug = sanitize_key( $args[2] );
$custom_template = WPRM_Template_Manager::get_template_by_slug( $template_slug );
}
if ( $custom_template ) {
// Add styling for this recipe's print template.
$output['assets'][] = array(
'type' => 'custom',
'html' => '<style type="text/css">' . WPRM_Template_Manager::get_template_css( $custom_template ) . '</style>',
);
}
$uid = 1;
$output['html'] = '';
foreach ( $unique_recipes as $unique_recipe ) {
if ( WPRM_POST_TYPE === get_post_type( $unique_recipe['id'] ) ) {
$recipe = WPRM_Recipe_Manager::get_recipe( $unique_recipe['id'] );
if ( ! self::has_permission( $recipe ) ) {
continue;
}
$recipes[] = array(
'id' => $uid,
'recipe_id' => $unique_recipe['id'],
'original_servings' => $recipe->servings(),
'servings' => 0 < $unique_recipe['servings'] ? $unique_recipe['servings'] : $recipe->servings(),
);
// Add styling for this recipe's print template.
if ( ! $custom_template ) {
$output['assets'][] = array(
'type' => 'custom',
'html' => WPRM_Template_Manager::get_template_styles( $recipe, 'print-collection' ),
);
}
$recipe_template = WPRM_Template_Manager::get_template( $recipe, 'print-collection', $custom_template['slug'] );
// Update ID for adjustable servings.
$recipe_template = str_replace( 'wprm-recipe-servings-' . $unique_recipe['id'], 'wprm-recipe-servings-' . $uid, $recipe_template );
$recipe_template = str_replace( 'wprm-recipe-adjustable-servings-' . $unique_recipe['id'] . '-container', 'wprm-recipe-adjustable-servings-' . $uid . '-container', $recipe_template );
$recipe_template = str_replace( 'wprm-recipe-advanced-servings-' . $unique_recipe['id'] . '-container', 'wprm-recipe-advanced-servings-' . $uid . '-container', $recipe_template );
// Update ID for linked ingriedents.
$recipe_template = str_replace( 'wprm-recipe-instruction-ingredient-' . $unique_recipe['id'] . '-', 'wprm-recipe-instruction-ingredient-' . $uid . '-', $recipe_template );
$recipe_template = str_replace( 'wprm-inline-ingredient-' . $unique_recipe['id'] . '-', 'wprm-inline-ingredient-' . $uid . '-', $recipe_template );
$output['html'] .= '<div id="wprm-print-recipe-' . $uid . '" data-original-recipe-id="' . $unique_recipe['id'] . '" data-recipe-id="' . $uid . '" class="wprm-print-recipe wprm-print-recipe-' . $uid . '" data-servings="' . esc_attr( $recipe->servings() ) . '">' . $recipe_template . '</div>';
$output['recipe_ids'][] = $unique_recipe['id'];
$uid++;
}
}
}
// Add options to header.
$output['header'] = self::print_header_images();
$output['type'] = 'recipes';
$output['recipes'] = $recipes;
}
return $output;
}
/**
* Get custom styling for the print accent color.
*
* @since 6.1.0
*/
public static function print_accent_color_styling() {
$output = '';
$color = WPRM_Settings::get( 'print_accent_color' );
$color_default = WPRM_Settings::get_default( 'print_accent_color' );
if ( $color !== $color_default ) {
$output .= '<style>';
$output .= ' #wprm-print-button-print { border-color: ' . $color . ' !important; background-color: ' . $color . ' !important; }';
$output .= ' .wprm-print-toggle:checked + label:before { border-color: ' . $color . ' !important; background: ' . $color . ' !important; }';
$output .= ' .wprm-print-option-container a.wprm-print-option { border-color: ' . $color . ' !important; }';
$output .= ' .wprm-print-option-container a.wprm-print-option.option-active { background-color: ' . $color . ' !important; }';
$output .= '</style>';
}
return $output;
}
/**
* Get print header image toggles.
*
* @since 6.1.0
* @param mixed $recipe Recipe getting printed.
*/
public static function print_header_images( $recipe = false ) {
$header = '';
// QR code toggle.
if ( WPRM_Settings::get( 'print_qr_code' ) && WPRM_Settings::get( 'print_qr_code_toggle' ) ) {
$header .= '<div class="wprm-print-toggle-container">';
$header .= '<input type="checkbox" id="wprm-print-toggle-qr" class="wprm-print-toggle" value="1" checked="checked"/><label for="wprm-print-toggle-qr">' . __( 'QR Code', 'wp-recipe-maker' ) . '</label>';
$header .= '</div>';
}
// Recipe image toggle.
if ( false === $recipe || $recipe->image() ) {
$checked = WPRM_Settings::get( 'print_show_recipe_image' ) ? 'checked="checked"' : '';
$header .= '<div class="wprm-print-toggle-container">';
$header .= '<input type="checkbox" id="wprm-print-toggle-recipe-image" class="wprm-print-toggle" value="1" ' . $checked . '/><label for="wprm-print-toggle-recipe-image">' . __( 'Recipe Image', 'wp-recipe-maker' ) . '</label>';
$header .= '</div>';
}
// Equipment toggle.
if ( false === $recipe || $recipe->equipment() ) {
$checked = WPRM_Settings::get( 'print_show_equipment' ) ? ' checked="checked"' : '';
$header .= '<div class="wprm-print-toggle-container">';
$header .= '<input type="checkbox" id="wprm-print-toggle-recipe-equipment" class="wprm-print-toggle" value="1" ' . $checked . '/><label for="wprm-print-toggle-recipe-equipment">' . __( 'Equipment', 'wp-recipe-maker' ) . '</label>';
$header .= '</div>';
}
// Ingredient images toggle.
$has_ingredient_images = false;
$ingredients_flat = $recipe ? $recipe->ingredients_flat() : array();
foreach( $ingredients_flat as $ingredient ) {
if ( isset( $ingredient['id'] ) && $ingredient['id'] ) {
$image_id = intval( get_term_meta( $ingredient['id'], 'wprmp_ingredient_image_id', true ) );
if ( $image_id ) {
$has_ingredient_images = true;
break;
}
}
}
if ( false === $recipe || $has_ingredient_images ) {
$checked = WPRM_Settings::get( 'print_show_ingredient_images' ) ? ' checked="checked"' : '';
$header .= '<div class="wprm-print-toggle-container">';
$header .= '<input type="checkbox" id="wprm-print-toggle-recipe-ingredient-media" class="wprm-print-toggle" value="1" ' . $checked . '/><label for="wprm-print-toggle-recipe-ingredient-media">' . __( 'Ingredient Images', 'wp-recipe-maker' ) . '</label>';
$header .= '</div>';
}
// Instruction images toggle.
$has_instructions_media = false;
$instructions_flat = $recipe ? $recipe->instructions_flat() : array();
foreach( $instructions_flat as $instruction ) {
if ( isset( $instruction['image'] ) && $instruction['image'] || ( isset( $instruction['video'] ) && isset( $instruction['video']['type'] ) && in_array( $instruction['video']['type'], array( 'upload', 'embed' ) ) ) ) {
$has_instructions_media = true;
break;
}
}
if ( false === $recipe || $has_instructions_media ) {
$checked = WPRM_Settings::get( 'print_show_instruction_images' ) ? ' checked="checked"' : '';
$header .= '<div class="wprm-print-toggle-container">';
$header .= '<input type="checkbox" id="wprm-print-toggle-recipe-instruction-media" class="wprm-print-toggle" value="1" ' . $checked . '/><label for="wprm-print-toggle-recipe-instruction-media">' . __( 'Instruction Images', 'wp-recipe-maker' ) . '</label>';
$header .= '</div>';
}
// Recipe notes toggle.
if ( false === $recipe || $recipe->notes() ) {
$checked = WPRM_Settings::get( 'print_show_notes' ) ? ' checked="checked"' : '';
$header .= '<div class="wprm-print-toggle-container">';
$header .= '<input type="checkbox" id="wprm-print-toggle-recipe-notes" class="wprm-print-toggle" value="1" ' . $checked . '/><label for="wprm-print-toggle-recipe-notes">' . __( 'Notes', 'wp-recipe-maker' ) . '</label>';
$header .= '</div>';
}
return $header;
}
/**
* Get output for the print page with low priority.
*
* @since 6.1.0
* @param array $output Current output for the print page.
* @param array $args Arguments for the print page.
*/
public static function output_last( $output, $args ) {
if ( $output ) {
// Add optional custom print CSS setting.
if ( isset( $output['assets'] ) ) {
$custom_print_css_setting = apply_filters( 'wprm_print_custom_css_setting', 'print_css', $args, $output );
$custom_print_css = WPRM_Settings::get( $custom_print_css_setting );
if ( $custom_print_css ) {
$output['assets'][] = array(
'type' => 'custom',
'html' => '<style>' . $custom_print_css . '</style>',
);
}
}
// Add optional QR code and print credit.
if ( 'recipe' === $args[0] && isset( $output['html'] ) ) {
$footer_html = '';
$recipe = WPRM_Recipe_Manager::get_recipe( intval( $args[1] ) );
// Optional print credit.
if ( WPRM_Settings::get( 'print_credit_use_html' ) ) {
$credit = WPRM_Settings::get( 'print_credit_html' );
} else {
$credit = WPRM_Settings::get( 'print_credit' );
}
if ( $credit ) {
$output['html'] .= '<div id="wprm-print-footer">' . $recipe->replace_placeholders( $credit ) . '</div>';
// Add class to indicate there's a print credit.
if ( ! isset( $output['classes'] ) ) {
$output['classes'] = array();
}
$output['classes'][] = 'wprm-print-has-footer';
}
// Optional QR Code.
if ( WPRM_Settings::get( 'print_qr_code' ) ) {
$url = $recipe->permalink();
if ( ! $url && WPRM_Settings::get( 'print_qr_code_use_homepage' ) ) {
$url = WPRM_Compatibility::get_home_url();
}
if ( $url ) {
// Generate QR code.
require_once( WPRM_DIR . 'vendor/phpqrcode/phpqrcode.php' );
$img = QRCode::png( $url );
$output['html'] .= '<div class="wprm-qr-code-container"><img class="wprm-qr-code" src="data:image/jpg;base64,' . base64_encode( $img ) . '" alt="' . esc_attr( __( 'QR Code linking back to recipe', 'wp-recipe-maker' ) ) . '"></div>';
$output['classes'][] = 'wprm-print-has-qr-code';
}
}
}
// Add option link back.
if ( isset( $output['url'] ) && $output['url'] ) {
$output['assets'][] = array(
'type' => 'custom',
'html' => '<script>var wprm_print_url = ' . wp_json_encode( $output['url'] ) . ';</script>',
);
}
// Print size options.
if ( WPRM_Settings::get( 'print_size_options' ) ) {
$output['header'] = isset( $output['header'] ) ? $output['header'] : '';
$output['header'] .= self::get_size_header();
}
}
return $output;
}
/**
* Get header for size options.
*
* @since 8.7.0
*/
private static function get_size_header() {
$output = '';
$output .= '<div id="wprm-print-size-container" class="wprm-print-option-container">';
$options = array(
'small' => __( 'Smaller', 'wp-recipe-maker' ),
'normal' => __( 'Normal', 'wp-recipe-maker' ),
'large' => __( 'Larger', 'wp-recipe-maker' ),
);
foreach ( $options as $value => $label ) {
$active = 'normal' === $value ? ' option-active' : '';
$output .= '<a href="#" role="button" class="wprm-print-size wprm-print-option' . $active . '" data-size="' . esc_attr( $value ) . '" aria-label="' . __( 'Make print size', 'wp-recipe-maker' ) . ' ' . esc_attr( $label ) . '">' . $label . '</a>';
}
$output .= '</div>';
return $output;
}
/**
* Bulk print recipes.
*
* @since 6.1.0
* @param array $recipe_ids IDs of the recipes to print.
*/
public static function bulk_print_url( $recipe_ids ) {
$ids_to_encode = array();
foreach( $recipe_ids as $id ) {
$ids_to_encode[] = $id;
$ids_to_encode[] = 0; // Use original servings.
}
$encoded = self::encode_ids( $ids_to_encode );
// Combine with function in class-wprm-recipe:
$home_url = WPRM_Compatibility::get_home_url();
$query_params = false;
if ( false !== strpos( $home_url, '?' ) ) {
$home_url_parts = explode( '?', $home_url, 2 );
$home_url = trailingslashit( $home_url_parts[0] );
$query_params = $home_url_parts[1];
}
$template = '';
if ( 'default_recipe_template' !== WPRM_Settings::get( 'default_print_template_admin' ) ) {
$template = '/' . WPRM_Settings::get( 'default_print_template_admin' );
}
if ( get_option( 'permalink_structure' ) ) {
$print_url = $home_url . WPRM_Print::slug() . '/recipes/' . $encoded . $template;
if ( $query_params ) {
$print_url .= '?' . $query_params;
}
} else {
$print_url = $home_url . '?' . WPRM_Print::slug() . '=recipes&' . $encoded . $template;
if ( $query_params ) {
$print_url .= '&' . $query_params;
}
}
return $print_url;
}
/**
* Encode recipe ids for printing.
*
* @since 6.1.0
* @param array $recipe_ids IDs of the recipes to encode for printing.
* @param mixed $servings Optional servings for these recipes.
*/
public static function encode_ids( $recipe_ids ) {
require_once( WPRM_DIR . 'vendor/hashids/lib/Hashids/HashGenerator.php' );
require_once( WPRM_DIR . 'vendor/hashids/lib/Hashids/Hashids.php' );
$hashids = new Hashids\Hashids('wp-recipe-maker');
return $hashids->encode( $recipe_ids );
}
/**
* Encode recipe ids from printed URL.
*
* @since 6.1.0
* @param mixed $encoded Encoded recipe ids.
*/
public static function decode_ids( $encoded ) {
require_once( WPRM_DIR . 'vendor/hashids/lib/Hashids/HashGenerator.php' );
require_once( WPRM_DIR . 'vendor/hashids/lib/Hashids/Hashids.php' );
$hashids = new Hashids\Hashids('wp-recipe-maker');
return $hashids->decode( $encoded );
}
/**
* Check if current user has the permissions to print a particular recipe.
*
* @since 6.3.0
* @param mixed $recipe Recipe to print.
*/
public static function has_permission( $recipe ) {
// Admin always has permission.
if ( current_user_can( 'administrator' ) ) {
return true;
}
// Simple check: is recipe published?
if ( WPRM_Settings::get( 'print_published_recipes_only' ) && 'publish' !== $recipe->post_status() ) {
return false;
}
// Advanced check: is recipe readable in parent post?
if ( WPRM_Settings::get( 'print_recipes_in_parent_content_only' ) ) {
$parent_post = $recipe->parent_post();
if ( $parent_post ) {
// Make sure the_content filter gets applied correctly (membership plugins).
$GLOBALS['post'] = $parent_post;
setup_postdata( $parent_post );
$post_content = apply_filters( 'the_content', $parent_post->post_content );
if ( false !== strpos( $post_content, 'id="wprm-recipe-container-' . $recipe->id() . '"') ) {
return true;
}
}
return false;
}
return true;
}
}
WPRM_Print::init();